const FULFILLED = 'Fulfilled';
const REJECTED = 'Rejected';
const PENDING = 'Pending';
class MyPromise {
    constructor(executor) {
        /**
         *  class中的constructor中this.属性和class类中直接定义属性有区别么？
         * 
         */
        this.status = PENDING;
        this.successCBs = [];
        this.successVal = null;
        this.failedCBs = [];
        this.failedReason = null;
        // 1. 判断执行器是否是个函数
        if (!isFunction(executor)) throw new Error("The executor must be a function!");
        // 2. 执行executor
        try {
            // 有没有其他办法免于bind this? resolve 和reject箭头函数
            // executor((this.resolve).bind(this), (this.reject).bind(this));
            executor(this.resolve, this.reject);
        } catch (error) {
            throw error;
        }
    }

    resolve = successVal => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.successVal = successVal;
        // console.log("Fulfilled: ", successVal);
        while (this.successCBs.length > 0) {
            this.successCBs.shift()(successVal);
        }
    }

    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.failedReason = reason;
        // console.log("Rejected: ", reason);
        while (this.failedCBs.length > 0) {
            this.failedCBs.shift()(reason);
        }
    }

    then(successCB, failedCB) {
        successCB = successCB ? successCB : val => val;
        failedCB = failedCB ? failedCB : reason => {
            throw reason
        };
        let p = new MyPromise((resolve, reject) => {
            switch (this.status) {
                case PENDING:
                    this.successCBs.push(() => {
                        setTimeout(() => {
                            try {
                                let x = successCB(this.successVal);
                                resolvePromise(p, x, resolve, reject);
                            } catch (error) {
                                reject(error);
                            }
                        }, 0);
                    });
                    this.failedCBs.push(() => {
                        setTimeout(() => {
                            try {
                                let x = failedCB(this.successVal);
                                resolvePromise(p, x, resolve, reject);
                            } catch (error) {
                                reject(error);
                            }
                        }, 0);
                    });
                    break;
                case FULFILLED:
                    setTimeout(() => {
                        try {
                            let x = successCB(this.successVal);
                            resolvePromise(p, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);

                    break;
                case REJECTED:
                    setTimeout(() => {
                        try {
                            let x = failedCB(this.successVal);
                            resolvePromise(p, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                    break;
                default:
                    break;
            }
        });
        return p;
    }
    catch = failedCB => {
        return this.then(undefined, failedCB);
    }
    finally = finallyCB => {
        return this.then(val => MyPromise.resolve(finallyCB()).then(() => val), reason => MyPromise.resolve(finallyCB()).then(() => {
            throw reason
        }));
    }
    static resolve(p) {
        if (p instanceof Promise) return p;
        return new MyPromise((resolve, reject) => {
            resolve(p);
        });
    }
    static reject(p) {
        if (p instanceof Promise) return p;
        return new MyPromise((resolve, reject) => {
            reject(p);
        });
    }
    static all(promises) {
        if (!isArray(promises)) throw new Error("Please transfer iterable objects!");
        let len = promises.length,
            count = 0,
            r = [];
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                const p = promises[i];
                if (p instanceof MyPromise) {
                    p.then(val => {
                        count++;
                        r[i] = val;
                        if (count === len) resolve(r);
                    }, reason => reject(reason));
                } else {
                    r[i] = p;
                    count++;
                    if (count === len) resolve(r);
                }
            }
        });
    }
    /**
     *  只返回第一个状态为fulfilled的promise的值，也包括rejected的值
     * @param {*} promises 
     * @returns 
     */
    static race(promises) {
        if (!isArray(promises)) throw new Error("Please transfer iterable objects!");
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                const p = promises[i];
                if (p instanceof MyPromise) {
                    p.then(val => {
                        resolve(val);
                    }, reason => reject(reason));
                } else {
                    resolve(p);
                }
            }
        });
    }

    static allSettled(promises) {
        if (!isArray(promises)) throw new Error("Please transfer iterable objects!");
        let len = promises.length,
            count = 0,
            r = [];
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                const p = promises[i];
                if (p instanceof MyPromise) {
                    p.then(val => {
                        count++;
                        r[i] = val;
                        if (count === len) resolve(r);
                    }, reason => {
                        count++;
                        r[i] = reason;
                        if (count === len) resolve(r);
                    });
                } else {
                    r[i] = p;
                    count++;
                    if (count === len) resolve(r);
                }
            }
        });
    }
    static any(promises) {
        if (!isArray(promises)) throw new Error("Please transfer iterable objects!");
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                const p = promises[i];
                if (p instanceof MyPromise) {
                    p.then(val => {
                        resolve(val);
                    }, reason => {

                    });
                } else {
                    resolve(val);
                }
            }
        });
    }
    /**
     * 只返回第一个状态为fulfilled的promise的值，不反悔rejected的值
     * 如果全部是rejected, 返回undefined
     * @returns 
     */
    static first() {
        let count = 0;
        if (!isArray(promises)) throw new Error("Please transfer iterable objects!");
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                const p = promises[i];
                if (p instanceof MyPromise) {
                    p.then(val => {
                        count++;
                        resolve(val);
                    }, reason => {
                        count++;
                        if (count = promises.length) reject("All promises not resolved")
                    });
                } else {
                    count++;
                    resolve(p);
                }
            }
        });
    }
    static last() {
        if (!isArray(promises)) throw new Error("Please transfer iterable objects!");
        let len = promises.length,
            count = 0;
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                const p = promises[i];
                if (p instanceof MyPromise) {
                    p.then(val => {
                        count++;
                        if (count === len) resolve(val);
                    }, reason => {
                        count++;
                        if (count === len) reject(reason);
                    });
                } else {
                    count++;
                    if (count === len) resolve(p);
                }
            }
        });
    }
    static none() {
        if (!isArray(promises)) throw new Error("Please transfer iterable objects!");
        let len = promises.length,
            count = 0,
            r = [];
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                const p = promises[i];
                if (p instanceof MyPromise) {
                    p.then(val => {
                        resolve(r);
                    }, reason => {
                        count++;
                        r[i] = reason;
                        if (count === len) resolve(r);
                    });
                } else {
                    r[i] = p;
                    count++;
                    if (count === len) resolve(r);
                }
            }
        });
    }

}

function resolvePromise(self, x, resolve, reject) {
    if (self === x) throw new Error("Cycle chain promise is not allowed!");
    if (x instanceof MyPromise) {
        x.then(resolve, reject);
    } else {
        resolve(x);
    }
}

function isFunction(fn) {
    return fn && Object.prototype.toString.call(fn) === '[object Function]';
}

function isArray(arr) {
    return arr && Object.prototype.toString.call(arr) === '[object Array]';
}